#
# mydbms/pysql_compiler/semantic_analyzer.py
#

from .ast import (
    ASTNode, SelectStatementNode, InsertStatementNode, CreateTableStatementNode,
    UpdateStatementNode, DeleteStatementNode, BinaryOperationNode, IdentifierNode,
    NumberNode, StringNode, StarNode, CreateIndexStatementNode,
    FunctionCallNode, AliasNode, TruncateTableStatementNode, InPredicateNode, JoinNode,
    UnaryOperationNode, LikePredicateNode, IsNullPredicateNode,
    # <<<--- 新增: 导入 DROP TABLE 节点 ---<<<
    DropTableStatementNode, UniqueConstraintNode, ColumnDefinitionNode,
    ForeignKeyConstraintNode
)
from .catalog import Catalog, SemanticError, TableSchema


class SemanticAnalyzer:
    """
    遍历AST并执行语义检查。
    这个阶段主要进行只读的检查，以验证SQL语句在逻辑上是否有效。
    唯一的例外是 CREATE TABLE，它会立即修改目录（Catalog），
    以便后续的语句可以立刻使用新创建的表。
    """

    def __init__(self, catalog: Catalog):
        self.catalog = catalog

    def analyze(self, ast_node: ASTNode):
        """分析主入口"""
        if isinstance(ast_node, CreateTableStatementNode):
            self._analyze_create_table(ast_node)
        elif isinstance(ast_node, InsertStatementNode):
            self._analyze_insert(ast_node)
        elif isinstance(ast_node, SelectStatementNode):
            self._analyze_select(ast_node)
        elif isinstance(ast_node, UpdateStatementNode):
            self._analyze_update(ast_node)
        elif isinstance(ast_node, DeleteStatementNode):
            self._analyze_delete(ast_node)
        elif isinstance(ast_node, CreateIndexStatementNode):
            self._analyze_create_index(ast_node)
        elif isinstance(ast_node, TruncateTableStatementNode):
            self._analyze_truncate_table(ast_node)
        # <<<--- 新增: 为 DROP TABLE 添加语义分析分支 ---<<<
        elif isinstance(ast_node, DropTableStatementNode):
            self._analyze_drop_table(ast_node)
        # >>>--- 新增结束 ---<<<
        return ast_node;

    def _analyze_truncate_table(self, node: TruncateTableStatementNode):
        """分析 TRUNCATE TABLE 语句"""
        table_name = node.table_name.value
        if not self.catalog.table_exists(table_name):
            raise SemanticError("存在性错误", (0, 0), f"目标表 '{table_name}' 不存在")
        print("[Semantic Analyzer] 'TRUNCATE TABLE' statement is valid.")

    # <<<--- 新增: DROP TABLE 的语义分析方法 ---<<<
    def _analyze_drop_table(self, node: DropTableStatementNode):
        """分析 DROP TABLE 语句"""
        table_name = node.table_name.value
        if not self.catalog.table_exists(table_name):
            raise SemanticError("存在性错误", (0, 0), f"目标表 '{table_name}' 不存在")
        print("[Semantic Analyzer] 'DROP TABLE' statement is valid.")

    # >>>--- 新增结束 ---<<<

    def _analyze_create_index(self, node: CreateIndexStatementNode):
        """
        分析 CREATE INDEX 语句的语义合法性。
        此阶段只进行检查，不修改 Catalog。实际的元数据创建将在执行阶段完成。
        """
        index_name = node.index_name.value
        table_name = node.table_name.value
        column_name = node.column_name.value

        if index_name in self.catalog.indexes:
            raise SemanticError("命名冲突", (0, 0), f"名为 '{index_name}' 的索引已经存在。")

        table_schema = self.catalog.get_table_schema(table_name)
        if not table_schema:
            raise SemanticError("引用错误", (0, 0), f"名为 '{table_name}' 的表不存在。")

        if not table_schema.has_column(column_name):
            raise SemanticError("引用错误", (0, 0), f"列 '{column_name}' 在表 '{table_name}' 中不存在。")

        print("[Semantic Analyzer] 'CREATE INDEX' statement is valid.")

    def _get_expression_type(self, node: ASTNode, schemas: list[TableSchema]) -> str:
        """在多个表的上下文中推断表达式结果的数据类型"""
        if isinstance(node, NumberNode):
            return 'INT' if isinstance(node.value, int) else 'FLOAT'
        elif isinstance(node, StringNode):
            return 'VARCHAR'
        elif isinstance(node, IdentifierNode):
            column_name = node.value
            if '.' in column_name:
                table_name, col_name = column_name.split('.')
                schema = self.catalog.get_table_schema(table_name)
                if not schema or not schema.has_column(col_name):
                    raise SemanticError("存在性错误", (0, 0), f"列 '{column_name}' 不存在")
                return schema.get_column_type(col_name)
            else:
                # 处理非限定列名，检查歧义
                found_schemas = [s for s in schemas if s.has_column(column_name)]
                if len(found_schemas) > 1:
                    raise SemanticError("命名冲突", (0, 0), f"列 '{column_name}' 存在于多个表中，引用不明确")
                if len(found_schemas) == 0:
                    raise SemanticError("存在性错误", (0, 0), f"列 '{column_name}' 在FROM子句的任何表中都不存在")
                return found_schemas[0].get_column_type(column_name)
        elif isinstance(node, FunctionCallNode):
            func_name = node.func_name.upper()
            if func_name == 'COUNT':
                return 'INT'
            elif func_name in ['SUM', 'AVG', 'MAX', 'MIN']:
                if node.args:
                    # 假定返回类型与参数类型相同（或更广泛的数字类型）
                    return self._get_expression_type(node.args[0], schemas)
                else:
                    raise SemanticError("聚合函数错误", (0, 0), f"聚合函数 '{func_name}' 需要一个参数。")
        return 'UNKNOWN'

    def _analyze_create_table(self, node: CreateTableStatementNode):
        """
        分析 CREATE TABLE 语句。
        - 检查表是否已存在。
        - 检查列名是否重复。
        - 检查表级约束（UNIQUE, FOREIGN KEY）中引用的列是否都存在。
        - 检查外键引用的表和列是否存在，且被引用的列是否具有唯一性。
        """
        table_name = node.table_name.value
        if self.catalog.table_exists(table_name):
            # 错误：表已经存在
            raise SemanticError("命名冲突", (0, 0), f"表 '{table_name}' 已经存在。")

        # 1. 首先收集所有明确定义的列名，为后续约束检查做准备
        column_definitions = [d for d in node.columns if isinstance(d, ColumnDefinitionNode)]
        column_names = {col.column_name.value for col in column_definitions}
        
        # 2. 检查是否有重复的列名
        if len(column_names) != len(column_definitions):
            raise SemanticError("命名冲突", (0, 0), "CREATE TABLE 语句中存在重复的列名定义。")

        # 3. 遍历所有定义（包括列定义和表级约束），进行详细的语义检查
        for definition in node.columns:
            
            if isinstance(definition, UniqueConstraintNode):
                # 检查表级 UNIQUE 约束
                if not definition.columns: continue  # 跳过行内 UNIQUE 约束
                for constraint_col in definition.columns:
                    if constraint_col.value not in column_names:
                        # 错误：UNIQUE约束中引用的列不存在
                        raise SemanticError("引用错误", (0, 0),
                            f"UNIQUE 约束中引用的列 '{constraint_col.value}' 在表 '{table_name}' 中不存在。")

            elif isinstance(definition, ForeignKeyConstraintNode):
                # 检查表级 FOREIGN KEY 约束
                ref_table_name = definition.ref_table.value
                ref_column_name = definition.ref_column.value

                # 检查点 1: 引用的表是否存在？
                ref_schema = self.catalog.get_table_schema(ref_table_name)
                if not ref_schema:
                    raise SemanticError("引用错误", (0, 0),
                        f"FOREIGN KEY 约束失败：引用的表 '{ref_table_name}' 不存在。")

                # 检查点 2: 引用的列在那个表中是否存在？
                if not ref_schema.has_column(ref_column_name):
                    raise SemanticError("引用错误", (0, 0),
                        f"FOREIGN KEY 约束失败：引用的列 '{ref_column_name}' 在表 '{ref_table_name}' 中不存在。")
                
                # 检查点 3: 引用的列是否是主键或具有唯一约束？
                # 在一个完整的DBMS中，这里会显式检查PRIMARY KEY。
                # 在我们当前的实现中，主键和唯一键都被记录在 unique_constraints 集合里，
                # 所以我们检查该列是否在这个集合中。
                if ref_column_name not in ref_schema.unique_constraints:
                    raise SemanticError("约束错误", (0, 0),
                        f"FOREIGN KEY 约束失败：引用的列 '{ref_column_name}' 在表 '{ref_table_name}' 中必须是 PRIMARY KEY 或带有 UNIQUE 约束。")

    def _analyze_insert(self, node: InsertStatementNode):
        """分析 INSERT 语句"""
        table_name = node.table.value
        if not self.catalog.table_exists(table_name):
            raise SemanticError("存在性错误", (0, 0), f"目标表 '{table_name}' 不存在")

        schema = self.catalog.get_table_schema(table_name)
        columns_to_insert = [c.value for c in node.columns] if node.columns else schema.column_order

        for values in node.values:
            if len(columns_to_insert) != len(values):
                raise SemanticError("列数不匹配", (0, 0),
                                    f"INSERT 语句中列的数量 ({len(columns_to_insert)}) 与值的数量 ({len(values)}) 不匹配")

            for i, col_name in enumerate(columns_to_insert):
                val_node = values[i]
                if not schema.has_column(col_name):
                    raise SemanticError("存在性错误", (0, 0), f"列 '{col_name}' 在表 '{table_name}' 中不存在")

                expected_type = schema.get_column_type(col_name)
                provided_type = self._get_expression_type(val_node, [schema])

                if (('INT' in expected_type or 'FLOAT' in expected_type) and provided_type not in ['INT', 'FLOAT']) or \
                        (('VARCHAR' in expected_type or 'TEXT' in expected_type) and provided_type != 'VARCHAR'):
                    raise SemanticError("类型不匹配", (0, 0),
                                        f"列 '{col_name}'期望类型为 {expected_type}，但提供的值类型为 {provided_type}")

        print("[Semantic Analyzer] 'INSERT' statement is valid.")

    def _analyze_select(self, node: SelectStatementNode):
        """分析 SELECT 语句"""
        schemas = []

        def collect_schemas(from_node):
            if isinstance(from_node, JoinNode):
                collect_schemas(from_node.left)
                collect_schemas(from_node.right)
            elif isinstance(from_node, IdentifierNode):
                schema = self.catalog.get_table_schema(from_node.value)
                if not schema:
                    raise SemanticError("存在性错误", (0, 0), f"目标表 '{from_node.value}' 不存在")
                schemas.append(schema)

        collect_schemas(node.from_clause)

        def check_expression(expr_node):
            if isinstance(expr_node, AliasNode):
                check_expression(expr_node.expression)
            elif isinstance(expr_node, FunctionCallNode):
                for arg in expr_node.args:
                    check_expression(arg)
            elif isinstance(expr_node, IdentifierNode):
                self._get_expression_type(expr_node, schemas)

        for col_node in node.columns:
            if isinstance(col_node, StarNode):
                continue
            check_expression(col_node)

        if node.where_clause:
            self._analyze_where_clause(node.where_clause, schemas)

        print("[Semantic Analyzer] 'SELECT' statement is valid.")

    def _analyze_where_clause(self, where_node, schemas: list[TableSchema]):
        """分析 WHERE / HAVING 子句"""
        if isinstance(where_node, InPredicateNode):
            # 检查 subquery 属性是否是一个AST节点 (即一个子查询)
            if isinstance(where_node.subquery, ASTNode):
                self.analyze(where_node.subquery)
                if len(where_node.subquery.columns) != 1 or not isinstance(where_node.subquery.columns[0],
                                                                           IdentifierNode):
                    raise SemanticError("子查询错误", (0, 0), "IN子句中的SELECT语句必须只选择一列。")
            # 如果是值列表 (list)，则在此阶段不进行额外检查
            return

        if isinstance(where_node, BinaryOperationNode):
            left_type = self._get_expression_type(where_node.left, schemas)
            right_type = self._get_expression_type(where_node.right, schemas)

            if (left_type in ['INT', 'FLOAT'] and right_type not in ['INT', 'FLOAT']) or \
                    (left_type == 'VARCHAR' and right_type != 'VARCHAR'):
                raise SemanticError("类型不匹配", (0, 0), f"条件子句中存在类型不兼容的比较：{left_type} 和 {right_type}")

    def _analyze_update(self, node: UpdateStatementNode):
        """分析 UPDATE 语句"""
        table_name = node.table.value
        if not self.catalog.table_exists(table_name):
            raise SemanticError("存在性错误", (0, 0), f"目标表 '{table_name}' 不存在")

        schema = self.catalog.get_table_schema(table_name)
        schemas = [schema]

        for assign_node in node.assignments:
            self._get_expression_type(assign_node.column, schemas)

        if node.from_table:
            from_table_name = node.from_table.value
            if not self.catalog.table_exists(from_table_name):
                raise SemanticError("存在性错误", (0, 0), f"FROM子句中的表 '{from_table_name}' 不存在")
            schemas.append(self.catalog.get_table_schema(from_table_name))

        if node.where_clause:
            self._analyze_where_clause(node.where_clause, schemas)

        print("[Semantic Analyzer] 'UPDATE' statement is valid.")

    def _analyze_delete(self, node: DeleteStatementNode):
        """分析 DELETE 语句"""
        table_name = node.table.value
        if not self.catalog.table_exists(table_name):
            raise SemanticError("存在性错误", (0, 0), f"目标表 '{table_name}' 不存在")

        schema = self.catalog.get_table_schema(table_name)

        if node.where_clause:
            self._analyze_where_clause(node.where_clause, [schema])

        print("[Semantic Analyzer] 'DELETE' statement is valid.")